home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / fortune.zip / fortune.c < prev    next >
Text File  |  1997-06-03  |  35KB  |  1,247 lines

  1. /*      $NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $  */
  2.  
  3. /*-
  4.  * Copyright (c) 1986, 1993
  5.  *      The Regents of the University of California.  All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to Berkeley by
  8.  * Ken Arnold.
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  * 1. Redistributions of source code must retain the above copyright
  14.  *    notice, this list of conditions and the following disclaimer.
  15.  * 2. Redistributions in binary form must reproduce the above copyright
  16.  *    notice, this list of conditions and the following disclaimer in the
  17.  *    documentation and/or other materials provided with the distribution.
  18.  * 3. All advertising materials mentioning features or use of this software
  19.  *    must display the following acknowledgement:
  20.  *      This product includes software developed by the University of
  21.  *      California, Berkeley and its contributors.
  22.  * 4. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38.  
  39. /* Modified September, 1995, Amy A. Lewis
  40.  * 1: removed all file-locking dreck.  Unnecessary
  41.  * 2: Fixed bug that made fortune -f report a different list than
  42.  *    fortune with any other parameters, or none, and which forced
  43.  *    the program to read only one file (named 'fortunes')
  44.  * 3: removed the unnecessary print_file_list()
  45.  * 4: Added "OFFDIR" to pathnames.h as the directory in which offensive
  46.  *    fortunes are kept.  This considerably simplifies our life by
  47.  *    permitting us to dispense with a lot of silly tests for the string
  48.  *    "-o" at the end of a filename.
  49.  * 5: I think the problems with trying to find filenames were fixed by
  50.  *    the change in the way that offensive files are defined.  Two birds,
  51.  *    one stone!
  52.  * 6: Calculated probabilities for all files, so that -f will print them.
  53.  */
  54.  
  55. #if 0                           /* comment out the stuff here, and get rid of silly warnings */
  56. #ifndef lint
  57. static char copyright[] =
  58. "@(#) Copyright (c) 1986, 1993\n\
  59.         The Regents of the University of California.  All rights reserved.\n";
  60.  
  61. #endif /* not lint */
  62.  
  63. #ifndef lint
  64. #if 0
  65. static char sccsid[] = "@(#)fortune.c   8.1 (Berkeley) 5/31/93";
  66.  
  67. #else
  68. static char rcsid[] = "$NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $";
  69.  
  70. #endif
  71. #endif /* not lint */
  72. #endif /* killing warnings */
  73.  
  74.  
  75. #include        <db.h>
  76. #include        <sys/types.h>
  77. #include        <strings.h>
  78.  
  79. #include        <sys/param.h>
  80. #include        <sys/stat.h>
  81. #include        <netinet/in.h>
  82.  
  83. #include        <time.h>
  84. #include        <dirent.h>
  85. #include        <fcntl.h>
  86. #include        <assert.h>
  87. #include        <unistd.h>
  88. #include        <stdio.h>
  89. #include        <ctype.h>
  90. #include        <stdlib.h>
  91. #include        <string.h>
  92. /*#include        <rx.h>*/
  93. #include        <regexp.h>
  94. #include        "strfile.h"
  95. #include        "pathnames.h"
  96.  
  97. #define TRUE    1
  98. #define FALSE   0
  99. #define bool    short
  100.  
  101. #define MINW    6               /* minimum wait if desired */
  102. #define CPERS   20              /* # of chars for each sec */
  103.  
  104. #define POS_UNKNOWN     ((off_t) -1)    /* pos for file unknown */
  105. #define NO_PROB         (-1)    /* no prob specified for file */
  106.  
  107. #ifdef DEBUG
  108. #define DPRINTF(l,x)    if (Debug >= l) fprintf x;
  109. #undef          NDEBUG
  110. #else
  111. #define DPRINTF(l,x)
  112. #define NDEBUG  1
  113. #endif
  114.  
  115. typedef struct fd
  116. {
  117.     int percent;
  118.     int fd, datfd;
  119.     off_t pos;
  120.     FILE *inf;
  121.     char *name;
  122.     char *path;
  123.     char *datfile, *posfile;
  124.     bool read_tbl;
  125.     bool was_pos_file;
  126.     STRFILE tbl;
  127.     int num_children;
  128.     struct fd *child, *parent;
  129.     struct fd *next, *prev;
  130. }
  131. FILEDESC;
  132.  
  133. bool Found_one;                 /* did we find a match? */
  134. bool Find_files = FALSE;        /* just find a list of proper fortune files */
  135. bool Wait = FALSE;              /* wait desired after fortune */
  136. bool Short_only = FALSE;        /* short fortune desired */
  137. bool Long_only = FALSE;         /* long fortune desired */
  138. bool Offend = FALSE;            /* offensive fortunes only */
  139. bool All_forts = FALSE;         /* any fortune allowed */
  140. bool Equal_probs = FALSE;       /* scatter un-allocated prob equally */
  141.  
  142. #ifndef NO_REGEX
  143. bool Match = FALSE;             /* dump fortunes matching a pattern */
  144.  
  145. #endif
  146. #ifdef DEBUG
  147. bool Debug = FALSE;             /* print debug messages */
  148.  
  149. #endif
  150.  
  151. unsigned char *Fortbuf = NULL;  /* fortune buffer for -m */
  152.  
  153. int Fort_len = 0, Spec_prob = 0,        /* total prob specified on cmd line */
  154.   Num_files, Num_kids,          /* totals of files and children. */
  155.   SLEN = 160;                   /* characters in a "short" fortune */
  156.  
  157. off_t Seekpts[2];               /* seek pointers to fortunes */
  158.  
  159. FILEDESC *File_list = NULL,     /* Head of file list */
  160.  *File_tail = NULL;             /* Tail of file list */
  161. FILEDESC *Fortfile;             /* Fortune file to use */
  162.  
  163. STRFILE Noprob_tbl;             /* sum of data for all no prob files */
  164.  
  165. #ifndef NO_REGEX
  166. #ifdef REGCMP
  167. #define RE_COMP(p)      (Re_pat = regcmp(p, NULL))
  168. #define BAD_COMP(f)     ((f) == NULL)
  169. #define RE_EXEC(p)      regex(Re_pat, (p))
  170.  
  171. char *Re_pat;
  172.  
  173. char *regcmp(), *regex();
  174.  
  175. #else
  176. /*#define RE_COMP(p)      (p = re_comp(p))*/
  177. #define RE_COMP(p)      (Re_pat = regcomp(p, NULL))
  178. /*#define BAD_COMP(f)     ((f) != NULL)*/
  179. #define BAD_COMP(f)     ((f) == NULL)
  180. /*#define RE_EXEC(p)      re_exec(p)*/
  181. #define RE_EXEC(p)      regexec(Re_pat, (p))
  182. regexp* Re_pat;
  183.  
  184. #endif
  185. #endif
  186.  
  187. int add_dir __P((register FILEDESC *));
  188.  
  189. void usage(void)
  190. {
  191.     (void) fprintf(stderr, "fortune [-a");
  192. #ifdef  DEBUG
  193.     (void) fprintf(stderr, "D");
  194. #endif /* DEBUG */
  195.     (void) fprintf(stderr, "f");
  196. #ifndef NO_REGEX
  197.     (void) fprintf(stderr, "i");
  198. #endif /* NO_REGEX */
  199.     (void) fprintf(stderr, "losw]");
  200. #ifndef NO_REGEX
  201.     (void) fprintf(stderr, " [-m pattern]");
  202. #endif /* NO_REGEX */
  203.     (void) fprintf(stderr, " [-n number] [ [#%%] file/directory/all]\n");
  204.     exit(1);
  205. }
  206.  
  207. #define STR(str)        ((str) == NULL ? "NULL" : (str))
  208.  
  209.  
  210. /*
  211.  * calc_equal_probs:
  212.  *      Set the global values for number of files/children, to be used
  213.  * in printing probabilities when listing files
  214.  */
  215. void calc_equal_probs(void)
  216. {
  217.     FILEDESC *fiddlylist;
  218.  
  219.     Num_files = Num_kids = 0;
  220.     fiddlylist = File_list;
  221.     while (fiddlylist != NULL)
  222.     {
  223.         Num_files++;
  224.         Num_kids += fiddlylist->num_children;
  225.         fiddlylist = fiddlylist->next;
  226.     }
  227. }
  228.  
  229. /*
  230.  * print_list:
  231.  *      Print out the actual list, recursively.
  232.  */
  233. void print_list(register FILEDESC * list, int lev)
  234. {
  235.     while (list != NULL)
  236.     {
  237.         fprintf(stderr, "%*s", lev * 4, "");
  238.         if (list->percent == NO_PROB)
  239.             if (!Equal_probs)
  240. /* This, with some changes elsewhere, gives proper percentages for every case
  241.  * fprintf(stderr, "___%%"); */
  242.                 fprintf(stderr, "%5.2f%%", (100.0 - Spec_prob) *
  243.                         list->tbl.str_numstr / Noprob_tbl.str_numstr);
  244.             else if (lev == 0)
  245.                 fprintf(stderr, "%5.2f%%", 100.0 / Num_files);
  246.             else
  247.                 fprintf(stderr, "%5.2f%%", 100.0 / Num_kids);
  248.         else
  249.             fprintf(stderr, "%5.2f%%", 1.0 * list->percent);
  250.         fprintf(stderr, " %s", STR(list->name));
  251.         DPRINTF(1, (stderr, " (%s, %s, %s)\n", STR(list->path),
  252.                     STR(list->datfile), STR(list->posfile)));
  253.         putc('\n', stderr);
  254.         if (list->child != NULL)
  255.             print_list(list->child, lev + 1);
  256.         list = list->next;
  257.     }
  258. }
  259.  
  260. #ifndef NO_REGEX
  261. /*
  262.  * conv_pat:
  263.  *      Convert the pattern to an ignore-case equivalent.
  264.  */
  265. char *conv_pat(register char *orig)
  266. {
  267.     register char *sp;
  268.     register unsigned int cnt;
  269.     register char *new;
  270.  
  271.     cnt = 1;                    /* allow for '\0' */
  272.     for (sp = orig; *sp != '\0'; sp++)
  273.         if (isalpha(*sp))
  274.             cnt += 4;
  275.         else
  276.             cnt++;
  277.     if ((new = malloc(cnt)) == NULL)
  278.     {
  279.         fprintf(stderr, "pattern too long for ignoring case\n");
  280.         exit(1);
  281.     }
  282.  
  283.     for (sp = new; *orig != '\0'; orig++)
  284.     {
  285.         if (islower(*orig))
  286.         {
  287.             *sp++ = '[';
  288.             *sp++ = *orig;
  289.             *sp++ = toupper(*orig);
  290.             *sp++ = ']';
  291.         }
  292.         else if (isupper(*orig))
  293.         {
  294.             *sp++ = '[';
  295.             *sp++ = *orig;
  296.             *sp++ = tolower(*orig);
  297.             *sp++ = ']';
  298.         }
  299.         else
  300.             *sp++ = *orig;
  301.     }
  302.     *sp = '\0';
  303.     return new;
  304. }
  305. #endif /* NO_REGEX */
  306.  
  307. /*
  308.  * do_malloc:
  309.  *      Do a malloc, checking for NULL return.
  310.  */
  311. void *do_malloc(unsigned int size)
  312. {
  313.     void *new;
  314.  
  315.     if ((new = malloc(size)) == NULL)
  316.     {
  317.         (void) fprintf(stderr, "fortune: out of memory.\n");
  318.         exit(1);
  319.     }
  320.     return new;
  321. }
  322.  
  323. /*
  324.  * do_free:
  325.  *      Free malloc'ed space, if any.
  326.  */
  327. void do_free(void *ptr)
  328. {
  329.     if (ptr != NULL)
  330.         free(ptr);
  331. }
  332.  
  333. /*
  334.  * copy:
  335.  *      Return a malloc()'ed copy of the string
  336.  */
  337. char *copy(char *str, unsigned int len)
  338. {
  339.     char *new, *sp;
  340.  
  341.     new = do_malloc(len + 1);
  342.     sp = new;
  343.     do
  344.     {
  345.         *sp++ = *str;
  346.     }
  347.     while (*str++);
  348.     return new;
  349. }
  350.  
  351. /*
  352.  * new_fp:
  353.  *      Return a pointer to an initialized new FILEDESC.
  354.  */
  355. FILEDESC *new_fp(void)
  356. {
  357.     register FILEDESC *fp;
  358.  
  359.     fp = (FILEDESC *) do_malloc(sizeof *fp);
  360.     fp->datfd = -1;
  361.     fp->pos = POS_UNKNOWN;
  362.     fp->inf = NULL;
  363.     fp->fd = -1;
  364.     fp->percent = NO_PROB;
  365.     fp->read_tbl = FALSE;
  366.     fp->next = NULL;
  367.     fp->prev = NULL;
  368.     fp->child = NULL;
  369.     fp->parent = NULL;
  370.     fp->datfile = NULL;
  371.     fp->posfile = NULL;
  372.     return fp;
  373. }
  374.  
  375. /*
  376.  * is_dir:
  377.  *      Return TRUE if the file is a directory, FALSE otherwise.
  378.  */
  379. int is_dir(char *file)
  380. {
  381.     auto struct stat sbuf;
  382.  
  383.     if (stat(file, &sbuf) < 0)
  384.         return FALSE;
  385.     return (sbuf.st_mode & S_IFDIR);
  386. }
  387.  
  388. /*
  389.  * is_fortfile:
  390.  *      Return TRUE if the file is a fortune database file.  We try and
  391.  *      exclude files without reading them if possible to avoid
  392.  *      overhead.  Files which start with ".", or which have "illegal"
  393.  *      suffixes, as contained in suflist[], are ruled out.
  394.  */
  395. int is_fortfile(char *file, char **datp, char **posp)
  396. {
  397.     register int i;
  398.     register char *sp;
  399.     register char *datfile;
  400.     static char *suflist[] =
  401.     {                           /* list of "illegal" suffixes" */
  402.         "dat", "pos", "c", "h", "p", "i", "f",
  403.         "pas", "ftn", "ins.c", "ins,pas",
  404.         "ins.ftn", "sml",
  405.         NULL
  406.     };
  407.  
  408.     DPRINTF(2, (stderr, "is_fortfile(%s) returns ", file));
  409.  
  410.     if ((sp = rindex(file, '/')) == NULL)
  411.         sp = file;
  412.     else
  413.         sp++;
  414.     if (*sp == '.')
  415.     {
  416.         DPRINTF(2, (stderr, "FALSE (file starts with '.')\n"));
  417.         return FALSE;
  418.     }
  419.     if ((sp = rindex(sp, '.')) != NULL)
  420.     {
  421.         sp++;
  422.         for (i = 0; suflist[i] != NULL; i++)
  423.             if (strcmp(sp, suflist[i]) == 0)
  424.             {
  425.                 DPRINTF(2, (stderr, "FALSE (file has suffix \".%s\")\n", sp));
  426.                 return FALSE;
  427.             }
  428.     }
  429.  
  430.     datfile = copy(file, (unsigned int) (strlen(file) + 4));    /* +4 for ".dat" */
  431.     strcat(datfile, ".dat");
  432.     if (access(datfile, R_OK) < 0)
  433.     {
  434.         free(datfile);
  435.         DPRINTF(2, (stderr, "FALSE (no \".dat\" file)\n"));
  436.         return FALSE;
  437.     }
  438.     if (datp != NULL)
  439.         *datp = datfile;
  440.     else
  441.         free(datfile);
  442.     DPRINTF(2, (stderr, "TRUE\n"));
  443.     return TRUE;
  444. }
  445.  
  446. /*
  447.  * add_file:
  448.  *      Add a file to the file list.
  449.  */
  450. int add_file(int percent, register char *file, char *dir,
  451.              FILEDESC ** head, FILEDESC ** tail, FILEDESC * parent)
  452. {
  453.     register FILEDESC *fp;
  454.     register int fd;
  455.     register char *path;
  456.     register bool was_malloc;
  457.     register bool isdir;
  458.  
  459.     if (dir == NULL)
  460.     {
  461.         path = file;
  462.         was_malloc = FALSE;
  463.     }
  464.     else
  465.     {
  466.         path = do_malloc((unsigned int) (strlen(dir) + strlen(file) + 2));
  467.         (void) strcat(strcat(strcpy(path, dir), "/"), file);
  468.         was_malloc = TRUE;
  469.     }
  470.     if ((isdir = is_dir(path)) && parent != NULL)
  471.     {
  472.         if (was_malloc)
  473.             free(path);
  474.         return FALSE;           /* don't recurse */
  475.     }
  476.  
  477.     DPRINTF(1, (stderr, "adding file \"%s\"\n", path));
  478.     if ((!is_dir(path) && (fd = open(path, 0)) < 0))
  479.     {
  480.         if (dir == NULL && file[0] != '/')
  481.             if (All_forts)
  482.                 return !(add_file(percent, file, FORTDIR, head, tail, parent)
  483.                      & add_file(percent, file, OFFDIR, head, tail, parent));
  484.             else if (Offend)
  485.                 return add_file(percent, file, OFFDIR, head, tail, parent);
  486.             else
  487.                 return add_file(percent, file, FORTDIR, head, tail, parent);
  488.         if (parent == NULL)
  489.             perror(path);
  490.         /* we really don't want an error message if the file might be in the other directory */
  491.         if (was_malloc)
  492.             free(path);
  493.         return FALSE;
  494.     } close(fd);
  495.  
  496.     DPRINTF(2, (stderr, "path = \"%s\"\n", path));
  497.  
  498.     fp = new_fp();
  499.     fp->fd = fd;
  500.     fp->percent = percent;
  501.     fp->name = file;
  502.     fp->path = path;
  503.     fp->parent = parent;
  504.  
  505.     if ((isdir && !add_dir(fp)) ||
  506.         (!isdir &&
  507.          !is_fortfile(path, &fp->datfile, &fp->posfile)))
  508.     {
  509.         if (parent == NULL)
  510.             fprintf(stderr,
  511.                     "fortune:%s not a fortune file or directory\n",
  512.                     path);
  513.         free((char *) fp);
  514.         if (was_malloc)
  515.             free(path);
  516.         do_free(fp->datfile);
  517.         do_free(fp->posfile);
  518.         return FALSE;
  519.     }
  520.     if (*head == NULL)
  521.         *head = *tail = fp;
  522.     else if (fp->percent == NO_PROB)
  523.     {
  524.         (*tail)->next = fp;
  525.         fp->prev = *tail;
  526.         *tail = fp;
  527.     }
  528.     else
  529.     {
  530.         (*head)->prev = fp;
  531.         fp->next = *head;
  532.         *head = fp;
  533.     }
  534.  
  535.     return TRUE;
  536. }
  537.  
  538. /*
  539.  * add_dir:
  540.  *      Add the contents of an entire directory.
  541.  */
  542. int add_dir(register FILEDESC * fp)
  543. {
  544.     register DIR *dir;
  545.     register struct dirent *dirent;
  546.     auto FILEDESC *tailp;
  547.     auto char *name;
  548.  
  549.     close(fp->fd);
  550.     fp->fd = -1;
  551.     if ((dir = opendir(fp->path)) == NULL)
  552.     {
  553.         perror(fp->path);
  554.         return FALSE;
  555.     }
  556.     tailp = NULL;
  557.     DPRINTF(1, (stderr, "adding dir \"%s\"\n", fp->path));
  558.     fp->num_children = 0;
  559.     while ((dirent = readdir(dir)) != NULL)
  560.     {
  561.         if (dirent->d_namlen == 0)
  562.             continue;
  563.         name = copy(dirent->d_name, dirent->d_namlen);
  564.         if (add_file(NO_PROB, name, fp->path, &fp->child, &tailp, fp))
  565.             fp->num_children++;
  566.         else
  567.             free(name);
  568.     }
  569.     if (fp->num_children == 0)
  570.     {
  571.         fprintf(stderr,
  572.                 "fortune: %s: No fortune files in directory.\n", fp->path);
  573.         return FALSE;
  574.     }
  575.     return TRUE;
  576. }
  577.  
  578. /*
  579.  * form_file_list:
  580.  *      Form the file list from the file specifications.
  581.  */
  582. int form_file_list(register char **files, register int file_cnt)
  583. {
  584.     register int i, percent;
  585.     register char *sp;
  586.  
  587.     if (file_cnt == 0)
  588.         if (All_forts)
  589.             return (add_file(NO_PROB, FORTDIR, NULL, &File_list,
  590.                              &File_tail, NULL)
  591.                     & add_file(NO_PROB, OFFDIR, NULL, &File_list,
  592.                                &File_tail, NULL));
  593.         else if (Offend)
  594.             return add_file(NO_PROB, OFFDIR, NULL, &File_list,
  595.                             &File_tail, NULL);
  596.         else
  597.             return add_file(NO_PROB, FORTDIR, NULL, &File_list,
  598.                             &File_tail, NULL);
  599.     for (i = 0; i < file_cnt; i++)
  600.     {
  601.         percent = NO_PROB;
  602.         if (!isdigit(files[i][0]))
  603.             sp = files[i];
  604.         else
  605.         {
  606.             percent = 0;
  607.             for (sp = files[i]; isdigit(*sp); sp++)
  608.                 percent = percent * 10 + *sp - '0';
  609.             if (percent > 100)
  610.             {
  611.                 fprintf(stderr, "percentages must be <= 100\n");
  612.                 return FALSE;
  613.             }
  614.             if (*sp == '.')
  615.             {
  616.                 fprintf(stderr, "percentages must be integers\n");
  617.                 return FALSE;
  618.             }
  619.             /*
  620.              * If the number isn't followed by a '%', then
  621.              * it was not a percentage, just the first part
  622.              * of a file name which starts with digits.
  623.              */
  624.             if (*sp != '%')
  625.             {
  626.                 percent = NO_PROB;
  627.                 sp = files[i];
  628.             }
  629.             else if (*++sp == '\0')
  630.             {
  631.                 if (++i >= file_cnt)
  632.                 {
  633.                     fprintf(stderr, "percentages must precede files\n");
  634.                     return FALSE;
  635.                 }
  636.                 sp = files[i];
  637.             }
  638.         }
  639.         if (strcmp(sp, "all") == 0)
  640.             sp = FORTDIR;
  641.         if (!add_file(percent, sp, NULL, &File_list, &File_tail, NULL))
  642.             return FALSE;
  643.     }
  644.     return TRUE;
  645. }
  646.  
  647. /*
  648.  *    This routine evaluates the arguments on the command line
  649.  */
  650. void getargs(int argc, char **argv)
  651. {
  652.     register int ignore_case;
  653.  
  654. #ifndef NO_REGEX
  655.     register char *pat = NULL;
  656.  
  657. #endif /* NO_REGEX */
  658.     extern char *optarg;
  659.     extern int optind;
  660.     int ch;
  661.  
  662.     ignore_case = FALSE;
  663.  
  664. #ifdef DEBUG
  665.     while ((ch = getopt(argc, argv, "aDefilm:n:osw")) != EOF)
  666. #else
  667.     while ((ch = getopt(argc, argv, "aefilm:n:osw")) != EOF)
  668. #endif /* DEBUG */
  669.         switch (ch)
  670.           {
  671.           case 'a':             /* any fortune */
  672.               All_forts++;
  673.               break;
  674. #ifdef DEBUG
  675.           case 'D':
  676.               Debug++;
  677.               break;
  678. #endif /* DEBUG */
  679.           case 'e':
  680.               Equal_probs++;    /* scatter un-allocted prob equally */
  681.               break;
  682.           case 'f':             /* find fortune files */
  683.               Find_files++;
  684.               break;
  685.           case 'l':             /* long ones only */
  686.               Long_only++;
  687.               Short_only = FALSE;
  688.               break;
  689.           case 'n':
  690.               SLEN = atoi(optarg);
  691.               break;
  692.           case 'o':             /* offensive ones only */
  693.               Offend++;
  694.               break;
  695.           case 's':             /* short ones only */
  696.               Short_only++;
  697.               Long_only = FALSE;
  698.               break;
  699.           case 'w':             /* give time to read */
  700.               Wait++;
  701.               break;
  702. #ifdef  NO_REGEX
  703.           case 'i':             /* case-insensitive match */
  704.           case 'm':             /* dump out the fortunes */
  705.               (void) fprintf(stderr,
  706.                   "fortune: can't match fortunes on this system (Sorry)\n");
  707.               exit(0);
  708. #else /* NO_REGEX */
  709.           case 'm':             /* dump out the fortunes */
  710.               Match++;
  711.               pat = optarg;
  712.               break;
  713.           case 'i':             /* case-insensitive match */
  714.               ignore_case++;
  715.               break;
  716. #endif /* NO_REGEX */
  717.           case '?':
  718.           default:
  719.               usage();
  720.           }
  721.     argc -= optind;
  722.     argv += optind;
  723.  
  724.     if (!form_file_list(argv, argc))
  725.         exit(1);                /* errors printed through form_file_list() */
  726. #ifdef DEBUG
  727. /*      if (Debug >= 1)
  728.  * print_list(File_list, 0); */
  729. #endif /* DEBUG */
  730. /* If (Find_files) print_list() moved to main */
  731. #ifndef NO_REGEX
  732.     if (pat != NULL)
  733.     {
  734.         if (ignore_case)
  735.             pat = conv_pat(pat);
  736.         if (BAD_COMP(RE_COMP(pat)))
  737.         {
  738. #ifndef REGCMP
  739.             fprintf(stderr, "%s\n", pat);
  740. #else /* REGCMP */
  741.             fprintf(stderr, "bad pattern: %s\n", pat);
  742. #endif /* REGCMP */
  743.         }
  744.     }
  745. #endif /* NO_REGEX */
  746. }
  747.  
  748. /*
  749.  * init_prob:
  750.  *      Initialize the fortune probabilities.
  751.  */
  752. void init_prob(void)
  753. {
  754.     register FILEDESC *fp, *last;
  755.     register int percent, num_noprob, frac;
  756.  
  757.     /*
  758.      * Distribute the residual probability (if any) across all
  759.      * files with unspecified probability (i.e., probability of 0)
  760.      * (if any).
  761.      */
  762.  
  763.     percent = 0;
  764.     num_noprob = 0;
  765.     last = NULL;
  766.     for (fp = File_tail; fp != NULL; fp = fp->prev)
  767.         if (fp->percent == NO_PROB)
  768.         {
  769.             num_noprob++;
  770.             if (Equal_probs)
  771.                 last = fp;
  772.         }
  773.         else
  774.             percent += fp->percent;
  775.     DPRINTF(1, (stderr, "summing probabilities:%d%% with %d NO_PROB's",
  776.                 percent, num_noprob));
  777.     if (percent > 100)
  778.     {
  779.         fprintf(stderr,
  780.                 "fortune: probabilities sum to %d%%!\n", percent);
  781.         exit(1);
  782.     }
  783.     else if (percent < 100 && num_noprob == 0)
  784.     {
  785.         fprintf(stderr,
  786.                 "fortune: no place to put residual probability (%d%%)\n",
  787.                 percent);
  788.         exit(1);
  789.     }
  790.     else if (percent == 100 && num_noprob != 0)
  791.     {
  792.         fprintf(stderr,
  793.                 "fortune: no probability left to put in residual files\n");
  794.         exit(1);
  795.     }
  796.     Spec_prob = percent;        /* this is for -f when % is specified on cmd line */
  797.     percent = 100 - percent;
  798.     if (Equal_probs)
  799.         if (num_noprob != 0)
  800.         {
  801.             if (num_noprob > 1)
  802.             {
  803.                 frac = percent / num_noprob;
  804.                 DPRINTF(1, (stderr, ", frac = %d%%", frac));
  805.                 for (fp = File_list; fp != last; fp = fp->next)
  806.                     if (fp->percent == NO_PROB)
  807.                     {
  808.                         fp->percent = frac;
  809.                         percent -= frac;
  810.                     }
  811.             }
  812.             last->percent = percent;
  813.             DPRINTF(1, (stderr, ", residual = %d%%", percent));
  814.         }
  815.         else
  816.         {
  817.             DPRINTF(1, (stderr,
  818.                         ", %d%% distributed over remaining fortunes\n",
  819.                         percent));
  820.         }
  821.     DPRINTF(1, (stderr, "\n"));
  822.  
  823. #ifdef DEBUG
  824. /*      if (Debug >= 1)
  825.      * print_list(File_list, 0); *//* Causes crash with new %% code */
  826. #endif
  827. }
  828.  
  829. /*
  830.  * zero_tbl:
  831.  *      Zero out the fields we care about in a tbl structure.
  832.  */
  833. void zero_tbl(register STRFILE * tp)
  834. {
  835.     tp->str_numstr = 0;
  836.     tp->str_longlen = 0;
  837.     tp->str_shortlen = -1;
  838. }
  839.  
  840. /*
  841.  * sum_tbl:
  842.  *      Merge the tbl data of t2 into t1.
  843.  */
  844. void sum_tbl(register STRFILE * t1, register STRFILE * t2)
  845. {
  846.     t1->str_numstr += t2->str_numstr;
  847.     if (t1->str_longlen < t2->str_longlen)
  848.         t1->str_longlen = t2->str_longlen;
  849.     if (t1->str_shortlen > t2->str_shortlen)
  850.         t1->str_shortlen = t2->str_shortlen;
  851. }
  852.  
  853. /*
  854.  * get_tbl:
  855.  *      Get the tbl data file the datfile.
  856.  */
  857. void get_tbl(FILEDESC * fp)
  858. {
  859.     auto int fd;
  860.     register FILEDESC *child;
  861.  
  862.     if (fp->read_tbl)
  863.         return;
  864.     if (fp->child == NULL)
  865.     {
  866.         if ((fd = open(fp->datfile, 0)) < 0)
  867.         {
  868.             perror(fp->datfile);
  869.             exit(1);
  870.         }
  871.         if (read(fd, (char *) &fp->tbl, sizeof fp->tbl) != sizeof fp->tbl)
  872.         {
  873.             fprintf(stderr,
  874.                     "fortune: %s corrupted\n", fp->path);
  875.             exit(1);
  876.         }
  877.         /* fp->tbl.str_version = ntohl(fp->tbl.str_version); */
  878.         fp->tbl.str_numstr = ntohl(fp->tbl.str_numstr);
  879.         fp->tbl.str_longlen = ntohl(fp->tbl.str_longlen);
  880.         fp->tbl.str_shortlen = ntohl(fp->tbl.str_shortlen);
  881.         fp->tbl.str_flags = ntohl(fp->tbl.str_flags);
  882.         close(fd);
  883.     }
  884.     else
  885.     {
  886.         zero_tbl(&fp->tbl);
  887.         for (child = fp->child; child != NULL; child = child->next)
  888.         {
  889.             get_tbl(child);
  890.             sum_tbl(&fp->tbl, &child->tbl);
  891.         }
  892.     }
  893.     fp->read_tbl = TRUE;
  894. }
  895.  
  896. /*
  897.  * sum_noprobs:
  898.  *      Sum up all the noprob probabilities, starting with fp.
  899.  */
  900. void sum_noprobs(register FILEDESC * fp)
  901. {
  902.     static bool did_noprobs = FALSE;
  903.  
  904.     if (did_noprobs)
  905.         return;
  906.     zero_tbl(&Noprob_tbl);
  907.     while (fp != NULL)
  908.     {
  909.         get_tbl(fp);
  910.         /* This conditional should help us return correct values for -f
  911.          * when a percentage is specified */
  912.         if (fp->percent == NO_PROB)
  913.             sum_tbl(&Noprob_tbl, &fp->tbl);
  914.         fp = fp->next;
  915.     }
  916.     did_noprobs = TRUE;
  917. }
  918.  
  919. /*
  920.  * pick_child
  921.  *      Pick a child from a chosen parent.
  922.  */
  923. FILEDESC *pick_child(FILEDESC * parent)
  924. {
  925.     register FILEDESC *fp;
  926.     register int choice;
  927.  
  928.     if (Equal_probs)
  929.     {
  930.         choice = random() % parent->num_children;
  931.         DPRINTF(1, (stderr, "    choice = %d (of %d)\n",
  932.                     choice, parent->num_children));
  933.         for (fp = parent->child; choice--; fp = fp->next)
  934.             continue;
  935.         DPRINTF(1, (stderr, "    using %s\n", fp->name));
  936.         return fp;
  937.     }
  938.     else
  939.     {
  940.         get_tbl(parent);
  941.         choice = random() % parent->tbl.str_numstr;
  942.         DPRINTF(1, (stderr, "    choice = %d (of %ld)\n",
  943.                     choice, parent->tbl.str_numstr));
  944.         for (fp = parent->child; choice >= fp->tbl.str_numstr;
  945.              fp = fp->next)
  946.         {
  947.             choice -= fp->tbl.str_numstr;
  948.             DPRINTF(1, (stderr, "\tskip %s, %ld (choice = %d)\n",
  949.                         fp->name, fp->tbl.str_numstr, choice));
  950.         }
  951.         DPRINTF(1, (stderr, "    using %s, %ld\n", fp->name,
  952.                     fp->tbl.str_numstr));
  953.         return fp;
  954.     }
  955. }
  956.  
  957. /*
  958.  * open_dat:
  959.  *      Open up the dat file if we need to.
  960.  */
  961. void open_dat(FILEDESC * fp)
  962. {
  963.     if (fp->datfd < 0 && (fp->datfd = open(fp->datfile, 0)) < 0)
  964.     {
  965.         perror(fp->datfile);
  966.         exit(1);
  967.     }
  968. }
  969.  
  970. /*
  971.  * get_pos:
  972.  *      Get the position from the pos file, if there is one.  If not,
  973.  *      return a random number.
  974.  */
  975. void get_pos(FILEDESC * fp)
  976. {
  977.     assert(fp->read_tbl);
  978.     if (fp->pos == POS_UNKNOWN)
  979.     {
  980.         fp->pos = random() % fp->tbl.str_numstr;
  981.     }
  982.     if (++(fp->pos) >= fp->tbl.str_numstr)
  983.         fp->pos -= fp->tbl.str_numstr;
  984.     DPRINTF(1, (stderr, "pos for %s is %ld\n", fp->name, fp->pos));
  985. }
  986.  
  987. /*
  988.  * get_fort:
  989.  *      Get the fortune data file's seek pointer for the next fortune.
  990.  */
  991. void get_fort(void)
  992. {
  993.     register FILEDESC *fp;
  994.     register int choice;
  995.     if (File_list->next == NULL || File_list->percent == NO_PROB)
  996.         fp = File_list;
  997.     else
  998.     {
  999.         choice = random() % 100;
  1000.         DPRINTF(1, (stderr, "choice = %d\n", choice));
  1001.         for (fp = File_list; fp->percent != NO_PROB; fp = fp->next)
  1002.             if (choice < fp->percent)
  1003.                 break;
  1004.             else
  1005.             {
  1006.                 choice -= fp->percent;
  1007.                 DPRINTF(1, (stderr,
  1008.                             "    skip \"%s\", %d%% (choice = %d)\n",
  1009.                             fp->name, fp->percent, choice));
  1010.             }
  1011.         DPRINTF(1, (stderr,
  1012.                     "using \"%s\", %d%% (choice = %d)\n",
  1013.                     fp->name, fp->percent, choice));
  1014.     }
  1015.     if (fp->percent != NO_PROB)
  1016.         get_tbl(fp);
  1017.     else
  1018.     {
  1019.         if (fp->next != NULL)
  1020.         {
  1021.             sum_noprobs(fp);
  1022.             choice = random() % Noprob_tbl.str_numstr;
  1023.             DPRINTF(1, (stderr, "choice = %d (of %ld) \n", choice,
  1024.                         Noprob_tbl.str_numstr));
  1025.             while (choice >= fp->tbl.str_numstr)
  1026.             {
  1027.                 choice -= fp->tbl.str_numstr;
  1028.                 fp = fp->next;
  1029.                 DPRINTF(1, (stderr,
  1030.                             "    skip \"%s\", %ld (choice = %d)\n",
  1031.                             fp->name, fp->tbl.str_numstr,
  1032.                             choice));
  1033.             }
  1034.             DPRINTF(1, (stderr, "using \"%s\", %ld\n", fp->name,
  1035.                         fp->tbl.str_numstr));
  1036.         }
  1037.         get_tbl(fp);
  1038.     }
  1039.     if (fp->child != NULL)
  1040.     {
  1041.         DPRINTF(1, (stderr, "picking child\n"));
  1042.         fp = pick_child(fp);
  1043.     }
  1044.     Fortfile = fp;
  1045.     get_pos(fp);
  1046.     open(fp->path, 0);
  1047.     open_dat(fp);
  1048.     lseek(fp->datfd,
  1049.           (off_t) (sizeof fp->tbl + fp->pos * sizeof Seekpts[0]), 0);
  1050.     read(fp->datfd, Seekpts, sizeof Seekpts);
  1051.     Seekpts[0] = ntohl(Seekpts[0]);
  1052.     Seekpts[1] = ntohl(Seekpts[1]);
  1053. }
  1054.  
  1055. /*
  1056.  * open_fp:
  1057.  *      Assocatiate a FILE * with the given FILEDESC.
  1058.  */
  1059. void open_fp(FILEDESC * fp)
  1060. {
  1061.     if (fp->inf == NULL && (fp->inf = fdopen(fp->fd, "r")) == NULL)
  1062.     {
  1063.         perror(fp->path);
  1064.         exit(1);
  1065.     }
  1066. }
  1067.  
  1068. #ifndef NO_REGEX
  1069. /*
  1070.  * maxlen_in_list
  1071.  *      Return the maximum fortune len in the file list.
  1072.  */
  1073. int maxlen_in_list(FILEDESC * list)
  1074. {
  1075.     register FILEDESC *fp;
  1076.     register int len, maxlen;
  1077.  
  1078.     maxlen = 0;
  1079.     for (fp = list; fp != NULL; fp = fp->next)
  1080.     {
  1081.         if (fp->child != NULL)
  1082.         {
  1083.             if ((len = maxlen_in_list(fp->child)) > maxlen)
  1084.                 maxlen = len;
  1085.         }
  1086.         else
  1087.         {
  1088.             get_tbl(fp);
  1089.             if (fp->tbl.str_longlen > maxlen)
  1090.                 maxlen = fp->tbl.str_longlen;
  1091.         }
  1092.     }
  1093.     return maxlen;
  1094. }
  1095.  
  1096. /*
  1097.  * matches_in_list
  1098.  *      Print out the matches from the files in the list.
  1099.  */
  1100. void matches_in_list(FILEDESC * list)
  1101. {
  1102.     unsigned char *sp;
  1103.     register FILEDESC *fp;
  1104.     int in_file;
  1105.  
  1106.     for (fp = list; fp != NULL; fp = fp->next)
  1107.     {
  1108.         if (fp->child != NULL)
  1109.         {
  1110.             matches_in_list(fp->child);
  1111.             continue;
  1112.         }
  1113.         DPRINTF(1, (stderr, "searching in %s\n", fp->path));
  1114.         fp->fd=open(fp->path, 0);
  1115.         open_fp(fp);
  1116.         sp = Fortbuf;
  1117.         in_file = FALSE;
  1118.         while (fgets(sp, Fort_len, fp->inf) != NULL)
  1119.             if (!STR_ENDSTRING(sp, fp->tbl))
  1120.                 sp += strlen(sp);
  1121.             else
  1122.             {
  1123.                 *sp = '\0';
  1124.                 if (RE_EXEC(Fortbuf))
  1125.                 {
  1126.                     if (!in_file)
  1127.                     {
  1128.                         fprintf(stderr, "(%s)\n%c\n", fp->name, fp->tbl.str_delim);
  1129.                         Found_one = TRUE;
  1130.                         in_file = TRUE;
  1131.                     }
  1132.                     fwrite(Fortbuf, 1, sp - Fortbuf, stdout);
  1133.                     printf("%c\n", fp->tbl.str_delim);
  1134.                 }
  1135.                 sp = Fortbuf;
  1136.             }
  1137.         close(fp->fd);
  1138.     }
  1139. }
  1140.  
  1141. /*
  1142.  * find_matches:
  1143.  *      Find all the fortunes which match the pattern we've been given.
  1144.  */
  1145. int find_matches(void)
  1146. {
  1147.     Fort_len = maxlen_in_list(File_list);
  1148.     DPRINTF(2, (stderr, "Maximum length is %d\n", Fort_len));
  1149.     /* extra length, "%\n" is appended */
  1150.     Fortbuf = do_malloc((unsigned int) Fort_len + 10);
  1151.  
  1152.     Found_one = FALSE;
  1153.     matches_in_list(File_list);
  1154.     return Found_one;
  1155.     /* NOTREACHED */
  1156. }
  1157. #endif /* NO_REGEX */
  1158.  
  1159. void display(FILEDESC * fp)
  1160. {
  1161.     register char *p, ch;
  1162.     unsigned char line[BUFSIZ];
  1163.  
  1164.     open_fp(fp);
  1165.     fseek(fp->inf, (long) Seekpts[0], 0);
  1166.     for (Fort_len = 0; fgets(line, sizeof line, fp->inf) != NULL &&
  1167.          !STR_ENDSTRING(line, fp->tbl); Fort_len++)
  1168.     {
  1169.         if (fp->tbl.str_flags & STR_ROTATED)
  1170.             for (p = line; (ch = *p); ++p)
  1171.                 if (isupper(ch))
  1172.                     *p = 'A' + (ch - 'A' + 13) % 26;
  1173.                 else if (islower(ch))
  1174.                     *p = 'a' + (ch - 'a' + 13) % 26;
  1175.         fputs(line, stdout);
  1176.     }
  1177.     fflush(stdout);
  1178. }
  1179.  
  1180. /*
  1181.  * fortlen:
  1182.  *      Return the length of the fortune.
  1183.  */
  1184. int fortlen(void)
  1185. {
  1186.     register int nchar;
  1187.     unsigned char line[BUFSIZ];
  1188.  
  1189.     if (!(Fortfile->tbl.str_flags & (STR_RANDOM | STR_ORDERED)))
  1190.         nchar = (Seekpts[1] - Seekpts[0]);      /* <= SLEN); huh? */
  1191.     else
  1192.     {
  1193.         open_fp(Fortfile);
  1194.         fseek(Fortfile->inf, (long) Seekpts[0], 0);
  1195.         nchar = 0;
  1196.         while (fgets(line, sizeof line, Fortfile->inf) != NULL &&
  1197.                !STR_ENDSTRING(line, Fortfile->tbl))
  1198.             nchar += strlen(line);
  1199.     }
  1200.     Fort_len = nchar;
  1201.     return nchar;
  1202. }
  1203.  
  1204. int max(register int i, register int j)
  1205. {
  1206.     return (i >= j ? i : j);
  1207. }
  1208.  
  1209. int main(int ac, char *av[])
  1210. {
  1211.     getargs(ac, av);
  1212.  
  1213. #ifndef NO_REGEX
  1214.     if (Match)
  1215.         exit(find_matches() != 0);
  1216. #endif
  1217.     init_prob();
  1218.     if (Find_files)
  1219.     {
  1220.         sum_noprobs(File_list);
  1221.         if (Equal_probs)
  1222.             calc_equal_probs();
  1223.         print_list(File_list, 0);
  1224.         exit(0);
  1225.     }
  1226.     srandom((int) (time((time_t *) NULL) + getpid()));
  1227.     do
  1228.     {
  1229.         get_fort();
  1230.         close(Fortfile->fd);
  1231.     }
  1232.     while ((Short_only && fortlen() > SLEN) ||
  1233.            (Long_only && fortlen() <= SLEN));
  1234.  
  1235.     open(Fortfile->path, 0);
  1236.     display(Fortfile);
  1237.  
  1238.     if (Wait)
  1239.     {
  1240.         if (Fort_len == 0)
  1241.             fortlen();
  1242.         sleep((unsigned int) max(Fort_len / CPERS, MINW));
  1243.     }
  1244.     exit(0);
  1245.     /* NOTREACHED */
  1246. }
  1247.